#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <UIKit/UIApplication.h>
#import <LockInfo/Plugin.h>
#import <EventKit/EKEvent.h>

#import "framework/FWCommon.h"
#import "framework/FWApplication.h"
#import "framework/FWPreferences.h"

#import "PICommon.h"
#import "PIIntegration.h"
#import "PIEventPlugin.h"

#import "PIEventIntegration.h"
#import "PIEventNative.h"
#import "PIEvent.h"
#import "PIAlarm.h"
#import "PIRecurrance.h"
#import "PIAttendee.h"


@implementation PIEventNative


// ******************************************************************************************
#pragma mark -
#pragma mark Fetch a list of basic events

+ (NSArray*)basicEventsList:(FWApplication*)application prefs:(FWPreferences*)prefs database:(FWDatabase*)database fromDate:(NSDate*)startDate toDate:(NSDate*)endDate
{
    NSBundle* bundle = [NSBundle bundleForClass:[self class]];
    NSDate* now = [NSDate date];
    
    EKEventStore* eventStore = ((PIEventPlugin*)application.fwPlugin).eventStore;
    NSArray* hiddenCals = [prefs arrayValue:kPIOptionsHiddenNativeCalendars withDefault:[NSArray array]];
    NSMutableArray* calendars = [NSMutableArray arrayWithCapacity:eventStore.calendars.count];
    DebugLog(@"LI:PIEvent: Filtering calendar list for events; hidden = %@", hiddenCals);
    for (EKCalendar* calendar in eventStore.calendars) {
        if ([hiddenCals indexOfObject:calendar.title] == NSNotFound) {
            DebugLog(@"LI:PIEvent: Calendar not in hidden list; %@", calendar);
            [calendars addObject:calendar];
        }
#ifdef DEBUG
        else
            DebugLog(@"LI:PIEvent: Calendar in hidden list - skipped; %@", calendar);
#endif
    }
    
    if (calendars.count == 0) {
        DebugLog(@"LI:PIEvent: No calendars left to check for native events");
        return [NSArray array];
    }

    // Get the calendar icons list that PI maintains
    NSDictionary* calendarIcons = [PIEventIntegration nativeCalendarIcons:application database:database calendars:calendars];

    XLog(@"LI:PIEvent: Fetching native events; start = %@; end = %@; calendars = %@", startDate, endDate, calendars);
    NSPredicate *predicate = [eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:calendars];
    
    // Fetch all events that match the predicate.
    NSArray* events = [eventStore eventsMatchingPredicate:predicate];
    NSMutableArray* results = [NSMutableArray arrayWithCapacity:[events count]];

    DebugLog(@"LI:PIEvent: Found %d native events", events.count);
    
    for (EKEvent* event in events) {
        NSString* calendarIcon = [calendarIcons objectForKey:FWKeyForCalendar(event.calendar)];
        PIEvent* basic = [PIEvent nativeEventWithKey:event.eventIdentifier
                                             version:[event.lastModifiedDate timeIntervalSinceReferenceDate]
                                               title:event.title ? event.title : localize(bundle, @"Event/New Event")
                                                icon:nil
                                            location:event.location
                                           startDate:event.startDate
                                        startDateRaw:[event.startDate timeIntervalSinceReferenceDate]
                                             endDate:event.endDate
                                          endDateRaw:[event.endDate timeIntervalSinceReferenceDate]
                                              allDay:event.isAllDay
                                            calendar:event.calendar.title
                                        calendarIcon:calendarIcon
                                       calendarColor:[UIColor colorWithCGColor:event.calendar.CGColor]];

        switch (event.availability) {
            case EKEventAvailabilityBusy:
                basic.availability = kPIEventAvailabilityBusy;
                break;
            case EKEventAvailabilityFree:
                basic.availability = kPIEventAvailabilityFree;
                break;
            case EKEventAvailabilityTentative:
                basic.availability = kPIEventAvailabilityTentative;
                break;
            case EKEventAvailabilityUnavailable:
                if (event.calendar.type == EKCalendarTypeExchange)
                    basic.availability = kPIEventAvailabilityOutOfOffice;
                else
                    basic.availability = kPIEventAvailabilityUnavailable;
                break;
            default:
                basic.availability = kPIEventAvailabilityNotSupported;
                break;
        }

        switch (event.status) {
            case EKEventStatusConfirmed:
                basic.status = PIEventStatusTypeConfirmed;
                break;
            case EKEventStatusTentative:
                basic.status = PIEventStatusTypeTentative;
                break;
            case EKEventStatusCanceled:
                basic.status = PIEventStatusTypeCancelled;
                break;
            default:
                basic.status = PIEventStatusTypeNone;
                break;
        }

        if ([event respondsToSelector:@selector(isInvite)]) {
            basic.invitation = [event isInvite] != nil;
            
            // Is invitation still outstanding?
            if (basic.invitation && [event respondsToSelector:@selector(isInvite)])
                basic.invitation = (int)[event participationStatus] == 1;
        }
        else if ([event respondsToSelector:@selector(participationStatus)]) {
            // The isInvite method is missing in iOS 5 - assume participationStatus is enough
            basic.invitation = (int)[event participationStatus] == 1;
        }

        basic.hasAttendees = event.attendees.count > 0;
        basic.repeating = event.recurrenceRule != nil;
        
        if (event.alarms && ([event.alarms count] > 0)) {
            NSDate* firstAlarm = nil;
            NSDate* nextAlarm = nil;
            
            for (EKAlarm* alarm in event.alarms) {
                NSDate* testAlarm = alarm.absoluteDate ? alarm.absoluteDate : [event.startDate addTimeInterval:alarm.relativeOffset];
                
                if (!firstAlarm || [firstAlarm isLaterThanDate:testAlarm])
                    firstAlarm = testAlarm;
                
                if ([testAlarm isLaterThanDate:now]) {
                    if (!nextAlarm || [nextAlarm isLaterThanDate:testAlarm])
                        nextAlarm = testAlarm;
                }
            }
            
            if (firstAlarm)
                basic.firstAlarm = firstAlarm;
            if (nextAlarm)
                basic.nextAlarm = nextAlarm;
        }
        
        [results addObject:basic];
    }
    
    return results;
}


+ (PIEvent*)augmentedBasicEvent:(PIEvent*)source application:(FWApplication*)application
{
    PIEvent* augment = [[source copy] autorelease];
    augment.augmented = YES;

    EKEventStore* eventStore = ((PIEventPlugin*)application.fwPlugin).eventStore;

    EKEvent* event = [eventStore eventWithIdentifier:augment.rootKey];
    
    if (event.organizer)
        augment.organiser = event.organizer.name;
    
    NSMutableArray* attendees = [NSMutableArray arrayWithCapacity:10];
    for (EKParticipant* attendee in event.attendees) {
        kPIParticipantStatusType status = kPIParticipantStatusUnknown;
        switch (attendee.participantStatus) {
            case EKParticipantStatusPending:
                status = kPIParticipantStatusPending;
                break;
            case EKParticipantStatusAccepted:
                status = kPIParticipantStatusAccepted;
                break;
            case EKParticipantStatusDeclined:
                status = kPIParticipantStatusDeclined;
                break;
            case EKParticipantStatusTentative:
                status = kPIParticipantStatusTentative;
                break;
            case EKParticipantStatusDelegated:
                status = kPIParticipantStatusDelegated;
                break;
            case EKParticipantStatusCompleted:
                status = kPIParticipantStatusCompleted;
                break;
            case EKParticipantStatusInProcess:
                status = kPIParticipantStatusInProgress;
                break;
            default:
                status = kPIParticipantStatusUnknown;
                break;
        }

        NSString* name = attendee.name;
        if (!name)
            name = [attendee.URL absoluteString];
        if ([name hasPrefix:@"mailto:"])
            name = [name substringFromIndex:7];

        [attendees addObject:
         [PIAttendee attendeeWithName:name status:status]];
    }
    
    if (attendees.count > 0) {
        [attendees sortUsingFunction:compareAttendees context:nil];
        augment.attendees = attendees;
        augment.hasAttendees = YES;
    }
    
    if (event.alarms && ([event.alarms count] > 0)) {
        NSMutableArray* alarms = [NSMutableArray arrayWithCapacity:event.alarms.count];
        for (EKAlarm* alarm in event.alarms) {
            PIAlarm* newAlarm = alarm.absoluteDate ?
              [PIAlarm alarmWithKey:@"" date:alarm.absoluteDate type:kPIEventAlarmTypeNative] :
              [PIAlarm alarmWithKey:@"" relativeDate:alarm.relativeOffset baseDate:event.startDate type:kPIEventAlarmTypeNative];
            [alarms addObject:newAlarm];
        }
        augment.alarms = alarms;
    }
    
    if (augment.isRepeating)
        augment.recurrance = [PIRecurrance recurranceWithRule:event.recurrenceRule];

    if (event.notes.length > 0)
        augment.notes = event.notes;
    
#ifdef DEVELOPMENT_MODE
    augment.notes = @"\n\n"
    "Here are notes that should have to be truncated.\n"
    "They are much too long to fit within the notes section as they are\n"
    "They also have two blank lines at the start which should be removed\n"
    "\n\n"
    "This is another line that ought to be bulking them out\n"
    "And here is another line that ought to be bulking them out\n"
    "Yet another line that ought to be bulking them out\n"
    "And finally another line that ought to be bulking them out\n"
    "There is also a phone number in this line - call 0800 234567 immediately!\n";
#endif
    
    return augment;
}


+ (void)showInApplication:(PIEvent*)event application:(FWApplication*)app
{
    PIEventPlugin* plugin = (PIEventPlugin*)app.fwPlugin;
    EKEventStore* eventStore = plugin.eventStore;
    if (EKEvent* native = [eventStore eventWithIdentifier:event.rootKey]) {
        if ([native respondsToSelector:@selector(externalURL)]) {
            NSURL* url = [native externalURL];
            
            NSLog(@"LI:%@: Displaying event: %@", app.resourceName, url);
            
            [plugin.liPlugin dismissPreview];
            [plugin.liPlugin performSelector:@selector(launchURL:) withObject:url afterDelay:0.5];
        }
    }
}

@end
